package com.autumn.mybatis.metadata;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.GenerationType;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.collections.map.CaseInsensitiveMap;

import com.autumn.mybatis.criterion.OrderDirectionEnum;
import com.autumn.mybatis.mapper.ISpecificationDefinition;
import com.autumn.mybatis.mapper.MapperException;
import com.autumn.mybatis.mapper.annotation.Specification;
import com.autumn.mybatis.mapper.annotation.TableEngine;
import com.autumn.mybatis.mapper.annotation.TableOrderBy;
import com.autumn.mybatis.mapper.annotation.ViewTable;
import com.autumn.mybatis.mapper.impl.FirstUpperCaseSpecificationDefinitionImpl;
import com.autumn.mybatis.mapper.impl.OriginalSpecificationDefinitionImpl;
import com.autumn.mybatis.mapper.impl.StandardSpecificationDefinitionImpl;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.StringUtils;
import com.autumn.util.TypeUtils;
import com.autumn.util.reflect.BeanProperty;
import com.autumn.util.reflect.ReflectUtils;
import com.autumn.util.tuple.TupleTwo;
import com.esotericsoftware.reflectasm.ConstructorAccess;

/**
 * 实体表
 * 
 * @author 杨昌国
 *
 *         2017-09-27 17:18:48
 */
public class EntityTable implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 2597268218663658578L;
	private static Map<Class<?>, EntityTable> entityTableMap = new ConcurrentHashMap<>();

	/**
	 * 获取表
	 * 
	 * @param entityClass
	 *            实体类型
	 * @return
	 * @author 杨昌国 2017-09-27 17:23:47
	 */
	public static EntityTable getTable(Class<?> entityClass) {
		return entityTableMap.computeIfAbsent(entityClass, k -> new EntityTable(k));
	}

	private final Class<?> entityClass;
	private final String name;
	private final String schema;
	private final List<EntityColumn> columns;
	private final Map<String, EntityColumn> propertyMap;
	private final Map<String, EntityColumn> columnMap;
	private final List<EntityColumnOrder> orderColumns;
	private final String fullName;
	private final ConstructorAccess<?> constructorAccess;
	private final List<EntityColumn> keyColumns;
	private final List<EntityIndex> indexs;
	private final String orderByClause;
	private final String engineName;
	private final boolean isView;
	private final String viewQueryStatement;

	/**
	 * 实例化 EntityTable 类新实例
	 * 
	 * @param entityClass
	 *            实体类
	 */
	private EntityTable(Class<?> entityClass) {
		this.entityClass = ExceptionUtils.checkNotNull(entityClass, "entityClass");
		ISpecificationDefinition specificationDefinition = getSpecificationDefinition(entityClass);
		Table table = entityClass.getAnnotation(Table.class);
		if (table != null) {
			if (StringUtils.isNullOrBlank(table.name())) {
				this.name = specificationDefinition.defaultTableName(TypeUtils.getClassShortName(entityClass));
			} else {
				this.name = table.name();
			}
			this.schema = table.schema();
		} else {
			this.name = specificationDefinition.defaultTableName(TypeUtils.getClassShortName(entityClass));
			this.schema = "";
		}
		if (StringUtils.isNullOrBlank(this.schema)) {
			this.fullName = this.name;
		} else {
			this.fullName = String.format("%s.%s", this.schema, this.name);
		}
		ViewTable viewTable = entityClass.getAnnotation(ViewTable.class);
		if (viewTable != null) {
			this.isView = true;
			this.viewQueryStatement = viewTable.value();
		} else {
			this.isView = false;
			this.viewQueryStatement = "";
		}
		TableEngine tableEngine = entityClass.getAnnotation(TableEngine.class);
		if (tableEngine != null) {
			this.engineName = tableEngine.value();
		} else {
			this.engineName = null;
		}
		this.columns = Collections.unmodifiableList(createColumns(this, entityClass, specificationDefinition));
		this.keyColumns = Collections.unmodifiableList(getKeyColumns(columns));
		//若非需要主键会造成部份视图不何用
		//if (this.keyColumns.size() == 0) {
		//	throw new MapperException(entityClass.getName() + " 至少需要配置一个主键。");
		//}
		this.propertyMap = Collections.unmodifiableMap(careatePropertyMap(this.columns));
		this.columnMap = Collections.unmodifiableMap(careateColumnMap(this.columns));
		TupleTwo<List<EntityColumnOrder>, String> orderTuple = createOrderColumns(entityClass, this.propertyMap);
		this.orderColumns = Collections.unmodifiableList(orderTuple.getItem1());
		this.orderByClause = orderTuple.getItem2();
		this.constructorAccess = ReflectUtils.getConstructorAccess(this.entityClass);
		this.indexs = Collections.unmodifiableList(EntityIndex.createEntityIndexs(this));
	}

	/**
	 * 
	 * @param entityClass
	 * @return
	 * @author 杨昌国 2017-12-04 11:51:30
	 */
	private static ISpecificationDefinition getSpecificationDefinition(Class<?> entityClass) {
		Specification specification = entityClass.getAnnotation(Specification.class);
		if (specification == null || specification.annotationType() == null) {
			return ISpecificationDefinition.DEFAULT_DEFINITION;
		}
		if (specification.annotationType().equals(StandardSpecificationDefinitionImpl.class)) {
			return ISpecificationDefinition.DEFAULT_DEFINITION;
		}
		if (specification.annotationType().equals(OriginalSpecificationDefinitionImpl.class)) {
			return ISpecificationDefinition.ORIGINAL_DEFINITION;
		}
		if (specification.annotationType().equals(FirstUpperCaseSpecificationDefinitionImpl.class)) {
			return ISpecificationDefinition.FIRST_UPPERCASE_DEFINITION;
		}
		try {
			return (ISpecificationDefinition) specification.annotationType().newInstance();
		} catch (Exception e) {
			throw ExceptionUtils.throwConfigureException("无法实例化类型：" + specification.annotationType());
		}
	}

	private List<EntityColumn> getKeyColumns(List<EntityColumn> cols) {
		List<EntityColumn> keyCols = new ArrayList<>();
		for (EntityColumn col : cols) {
			if (col.isPrimaryKey()) {
				keyCols.add(col);
			}
		}
		return keyCols;
	}

	private static Map<String, EntityColumn> careatePropertyMap(List<EntityColumn> cols) {
		Map<String, EntityColumn> map = new HashMap<>(16);
		for (EntityColumn col : cols) {
			map.put(col.getPropertyName(), col);
		}
		return map;
	}

	@SuppressWarnings("unchecked")
	private static Map<String, EntityColumn> careateColumnMap(List<EntityColumn> cols) {
		Map<String, EntityColumn> map = new CaseInsensitiveMap();
		for (EntityColumn col : cols) {
			map.put(col.getColumnName(), col);
		}
		return map;
	}

	/**
	 * 
	 * @param entityClass
	 * @param propertyMap
	 * @return
	 */
	private TupleTwo<List<EntityColumnOrder>, String> createOrderColumns(Class<?> entityClass,
			Map<String, EntityColumn> propertyMap) {
		List<EntityColumnOrder> columns = new ArrayList<>();
		StringBuilder orderStr = new StringBuilder();
		TableOrderBy tableOrderBy = entityClass.getAnnotation(TableOrderBy.class);
		if (tableOrderBy != null && !StringUtils.isNullOrBlank(tableOrderBy.value())) {
			String[] items = tableOrderBy.value().split("\\,");
			for (String item : items) {
				if (!StringUtils.isNullOrBlank(item)) {
					String[] orders = item.trim().split("\\s+");
					EntityColumn col = propertyMap.get(orders[0]);
					if (col == null) {
						throw new MapperException(entityClass.getCanonicalName() + " 的排序属性 " + col + " 不存在!。");
					}
					if (orders.length > 1) {
						String order = orders[1];
						OrderDirectionEnum direction;
						if ("asc".equalsIgnoreCase(order)) {
							direction = OrderDirectionEnum.ASC;
						} else if ("desc".equalsIgnoreCase(order)) {
							direction = OrderDirectionEnum.DESC;
						} else {
							throw new MapperException(
									entityClass.getCanonicalName() + " 的排序属性 " + col + " 的排序方式只能支持 Asc 或 Desc。");
						}
						EntityColumnOrder orderCol = new EntityColumnOrder(col, direction);
						columns.add(orderCol);
						if (orderStr.length() > 0) {
							orderStr.append(",");
						}
						orderStr.append(orderCol.toString());
					}
				}
			}
		}
		return new TupleTwo<List<EntityColumnOrder>, String>(columns, orderStr.toString());
	}

	/**
	 * 创建列集合
	 * 
	 * @param entityClass
	 *            实体类型
	 * @return
	 */
	private static List<EntityColumn> createColumns(EntityTable table, Class<?> entityClass,
			ISpecificationDefinition specificationDefinition) {
		List<EntityColumn> cols = new ArrayList<>();
		List<BeanProperty> propertys = ReflectUtils.findBeanPropertys(entityClass);
		int identityCount = 0;
		for (BeanProperty property : propertys) {
			if (TypeUtils.isBaseOrBinaryType(property.getType())) {
				if (!property.isAnnotation(Transient.class)) {
					EntityColumn column = new EntityColumn(table, property, specificationDefinition);
					if (column.isPrimaryKey() && column.getGenerationType().equals(GenerationType.IDENTITY)) {
						identityCount++;
						if (identityCount > 1) {
							throw new MapperException(entityClass.getCanonicalName() + " 主键自动类型为 Identity 最多只支持一个。");
						}
					}
					cols.add(column);
				}
			}
		}
		Collections.sort(cols);
		return cols;
	}

	/**
	 * 创建实体新实例
	 * 
	 * @return
	 */
	public Object newEntityInstance() {
		return this.constructorAccess.newInstance();
	}

	/**
	 * 获取实体类型
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:20:15
	 */
	public final Class<?> getEntityClass() {
		return entityClass;
	}

	/**
	 * 获取列集合
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:50:28
	 */
	public final List<EntityColumn> getColumns() {
		return columns;
	}

	/**
	 * 获取属性 Map
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:52:44
	 */
	public final Map<String, EntityColumn> getPropertyMap() {
		return propertyMap;
	}

	/**
	 * 获取列的 Map(不区分大小写)
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:52:51
	 */
	public final Map<String, EntityColumn> getColumnMap() {
		return columnMap;
	}

	/**
	 * 获取主键列集合
	 * 
	 * @return
	 */
	public final List<EntityColumn> getKeyColumns() {
		return keyColumns;
	}

	/**
	 * 获取索引集合
	 * 
	 * @return
	 */
	public final List<EntityIndex> getIndexs() {
		return indexs;
	}

	/**
	 * 获取表名称
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:58:48
	 */
	public final String getName() {
		return name;
	}

	/**
	 * 获取架构
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:58:54
	 */
	public final String getSchema() {
		return schema;
	}

	/**
	 * 是否是视图
	 * 
	 * @return
	 */
	public final boolean isView() {
		return isView;
	}

	/**
	 * 获取视图查询命令
	 * 
	 * @return
	 */
	public String getViewQueryStatement() {
		return this.viewQueryStatement;
	}

	/**
	 * 获取完整名称
	 * 
	 * @return
	 */
	public String getFullName() {
		return fullName;
	}

	/**
	 * 获取引擎名称
	 * 
	 * @return
	 *
	 */
	public String getEngineName() {
		return engineName;
	}

	@Override
	public String toString() {
		return this.getFullName();
	}

	/**
	 * 获取排序列
	 * 
	 * @return
	 */
	public List<EntityColumnOrder> getOrderColumns() {
		return orderColumns;
	}

	/**
	 * 获取排序
	 * 
	 * @return
	 */
	public String getOrderByClause() {
		return orderByClause;
	}

	/**
	 * 获取主键属性名称集
	 * 
	 * @return
	 */
	public String[] getKeyPropertieNames() {
		List<EntityColumn> keyCols = this.getKeyColumns();
		String[] keys = new String[keyCols.size()];
		for (int i = 0; i < keyCols.size(); i++) {
			keys[i] = keyCols.get(i).getPropertyName();
		}
		return keys;
	}

	/**
	 * 获取主键列名称集
	 * 
	 * @return
	 */
	public String[] getKeyColumnNames() {
		List<EntityColumn> keyCols = this.getKeyColumns();
		String[] keys = new String[keyCols.size()];
		for (int i = 0; i < keyCols.size(); i++) {
			keys[i] = keyCols.get(i).getColumnName();
		}
		return keys;
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return super.equals(obj);
	}
}
